package com.xlabs.client;

import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.users.UserService;
import com.google.appengine.api.users.UserServiceFactory;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;

import javax.jdo.PersistenceManager;
import javax.jdo.Query;
import java.util.*;


/**
 * TimedCommentServiceImpl class.
 */
public class CommentingServiceImpl extends RemoteServiceServlet implements
        CommentingService {

    /**
     * Posts comment for a video.
     *
     * @param comment        comment text.
     * @param time           time of comment.
     * @param currentVideoId video id.
     */
    public CommentDTO[] postQuestion(String comment, double time, String currentVideoId) {

        Video.addQuestionToVideo(comment, time, currentVideoId);

        return CommentDO.getAllQuestions();
    }

    /**
     * Gets all comments for the given video.
     *
     * @param currentVideoId video id.
     */
    public CommentDTO[] getQuestions(String currentVideoId) {
        PersistenceManager pm = PMF.get().getPersistenceManager();
        CommentDTO[] result;

        try {

            Video.AddVideo(currentVideoId);

            result = CommentDO.getAllQuestions();

        }
        finally {

            pm.close();
        }

        return result;
    }

    /**
     * Posts a reply for a question.
     *
     * @param reply             reply.
     * @param questionStringKey question key.
     * @return updated comment.
     */
    public CommentDTO[] postReply(String reply, String questionStringKey) {
        PersistenceManager pm = PMF.get().getPersistenceManager();

        try {
            Key questionKey = KeyFactory.stringToKey(questionStringKey);
            CommentDO questionDO = pm.getObjectById(CommentDO.class, questionKey);

            CommentDO replyDO = new CommentDO(reply, 0, new Date());

            replyDO.setReplyTo(questionKey);
            replyDO.setReply((Boolean) true);

            UserService userService = UserServiceFactory.getUserService();
            replyDO.setCreatedBy(userService.getCurrentUser());

            if (questionDO.getRelatedTo() != null) {
                replyDO.setRelatedTo(questionDO.getRelatedTo());
            }
            else {
                replyDO.setRelatedTo(questionKey);
            }

            pm.makePersistent(replyDO);

            questionDO.addReply(replyDO.getKey());
        }
        finally {
            pm.close();
        }

        return CommentDO.getAllQuestions();
    }

    /**
     * Gets the conversation related to the question given.
     *
     * @param questionEncodedStringKey question's key.
     * @return conversation.
     */
    public List<CommentDTO> getConversation(String questionEncodedStringKey) {
        PersistenceManager pm = PMF.get().getPersistenceManager();

        Key questionKey = KeyFactory.stringToKey(questionEncodedStringKey);

        List<CommentDO> orderedReplies = new ArrayList<CommentDO>();

        try {
            Query query = pm.newQuery(CommentDO.class);
            query.setFilter("relatedTo == questionKey");
            query.setOrdering("createdOn asc");
            query.declareParameters("String questionKey");

            try {
                orderedReplies = (List<CommentDO>) query.execute(questionKey);
            }
            finally {
                query.closeAll();
            }

            CommentDO questionDO = pm.getObjectById(CommentDO.class, questionKey);

            return this.createConversationTree(orderedReplies, questionDO);
        }
        finally {
            pm.close();
        }
    }

    /**
     * Orders the replies and return them.
     *
     * @param orderedReplies List of unordered replies.
     * @param question         Parent question.
     * @return Ordered replies.
     */
    private List<CommentDTO> createConversationTree(List<CommentDO> orderedReplies, CommentDO question) {
        List<CommentDTO> result = new ArrayList<CommentDTO>(orderedReplies.size());

        Map<Key, CommentDO> commentsMap = new HashMap<Key, CommentDO>(orderedReplies.size() + 1);

        Map<Key, CommentDTO> commentsUIMap = new HashMap<Key, CommentDTO>(orderedReplies.size() + 1);

        commentsMap.put(question.getKey(), question);
        CommentDTO questionDTO = question.toCommentDTO();
        commentsUIMap.put(question.getKey(), questionDTO);
        result.add(questionDTO);
        
        for (CommentDO reply : orderedReplies) {
            commentsMap.put(reply.getKey(), reply);
        }

        for (CommentDO reply : orderedReplies)
        {
            CommentDTO replyDTO = reply.toCommentDTO();
            
            commentsUIMap.put(reply.getKey(), replyDTO);

            Key replyToKey = reply.getReplyTo();
            replyDTO.setParent(commentsUIMap.get(replyToKey));

            result.add(replyDTO);
        }

        return result;
    }
}